/*-------------------------------------------------------------------------
 *
 * nodeFunctionscan.c
 *	  扫描RangeFunctions（rangetable中的函数）的支持例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeFunctionscan.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * 接口例程
 *		ExecFunctionScan		扫描函数。
 *		ExecFunctionNext		按顺序检索下一个元组。
 *		ExecInitFunctionScan	创建并初始化函数扫描节点。
 *		ExecEndFunctionScan	释放任何分配的存储。
 *		ExecReScanFunctionScan	重新扫描函数
 */
#include "postgres.h"

#include "catalog/pg_type.h"
#include "executor/nodeFunctionscan.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
#include "utils/builtins.h"
#include "utils/memutils.h"


/*
 * 每个正在扫描的函数的运行时数据。
 */
typedef struct FunctionScanPerFuncState
{
	SetExprState *setexpr;		/* 正在评估的表达式的状态 */
	TupleDesc	tupdesc;		/* 函数返回类型的描述 */
	int			colcount;		/* 预期的结果列数 */
	Tuplestorestate *tstore;	/* 保存函数结果集 */
	int64		rowcount;		/* 结果集中行的数量，如果未知则为-1 */
	TupleTableSlot *func_slot;	/* 函数结果槽（或NULL） */
} FunctionScanPerFuncState;

static TupleTableSlot *fc_FunctionNext(FunctionScanState *fc_node);


/* ----------------------------------------------------------------
 *						扫描支持
 * ----------------------------------------------------------------
 */
/* ----------------------------------------------------------------
 *		FunctionNext
 *
 *		这是ExecFunctionScan的工作马
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_FunctionNext(FunctionScanState *fc_node)
{
	EState	   *fc_estate;
	ScanDirection fc_direction;
	TupleTableSlot *fc_scanslot;
	bool		fc_alldone;
	int64		fc_oldpos;
	int			fc_funcno;
	int			fc_att;

	/*
	 * 从estate和扫描状态获取信息
	 */
	fc_estate = fc_node->ss.ps.state;
	fc_direction = fc_estate->es_direction;
	fc_scanslot = fc_node->ss.ss_ScanTupleSlot;

	if (fc_node->simple)
	{
		/*
		 * 对于简单情况的快速路径：函数返回类型和扫描结果类型相同，因此我们将函数结果直接提取到扫描结果槽中。也无需更新序数或
		 * 行计数。
		 */
		Tuplestorestate *fc_tstore = fc_node->funcstates[0].tstore;

		/*
		 * 如果是第一次读取，则从函数中读取所有元组并将其放入元组存储中。后续调用只需从
		 * 元组存储中获取元组。
		 */
		if (fc_tstore == NULL)
		{
			fc_node->funcstates[0].tstore = fc_tstore =
				ExecMakeTableFunctionResult(fc_node->funcstates[0].setexpr,
											fc_node->ss.ps.ps_ExprContext,
											fc_node->argcontext,
											fc_node->funcstates[0].tupdesc,
											fc_node->eflags & EXEC_FLAG_BACKWARD);

			/*
			 * 幸存 - 如果函数可能构造了
			 * 自身的元组存储，并且没有将其指向开始，则应对这种情况。这
			 * 次调用很快，因此开销不应该是个问题。
			 */
			tuplestore_rescan(fc_tstore);
		}

		/*
		 * 从元组存储中获取下一个元组。
		 */
		(void) tuplestore_gettupleslot(fc_tstore,
									   ScanDirectionIsForward(fc_direction),
									   false,
									   fc_scanslot);
		return fc_scanslot;
	}

	/*
	 * 在检查数据结束之前增加或减少序数计数器，以便我们可以从结果的任一端移动1（而且不超过
	 * 1），而不会丢失正确计数。请参见PortalRunSelect，了解为什么我们可以
	 * 假设在数据结束状态下不会被重复调用。
	 */
	fc_oldpos = fc_node->ordinal;
	if (ScanDirectionIsForward(fc_direction))
		fc_node->ordinal++;
	else
		fc_node->ordinal--;

	/*
	 * 函数的主循环。
	 *
	 * 我们将函数结果提取到func_slots（与函数返回类型匹配），然后将值复制到scanslot（与
	 * 扫描结果类型匹配），同时设置序数列（如果有的话）。
	 */
	ExecClearTuple(fc_scanslot);
	fc_att = 0;
	fc_alldone = true;
	for (fc_funcno = 0; fc_funcno < fc_node->nfuncs; fc_funcno++)
	{
		FunctionScanPerFuncState *fc_fs = &fc_node->funcstates[fc_funcno];
		int			fc_i;

		/*
		 * 如果是第一次读取，则从函数中读取所有元组并将其放入元组存储中。后续调用只需从
		 * 元组存储中获取元组。
		 */
		if (fc_fs->tstore == NULL)
		{
			fc_fs->tstore =
				ExecMakeTableFunctionResult(fc_fs->setexpr,
											fc_node->ss.ps.ps_ExprContext,
											fc_node->argcontext,
											fc_fs->tupdesc,
											fc_node->eflags & EXEC_FLAG_BACKWARD);

			/*
			 * 幸存 - 如果函数可能构造了
			 * 自身的元组存储，并且没有将其指向开始，则应对这种情况。这
			 * 次调用很快，因此开销不应该是个问题。
			 */
			tuplestore_rescan(fc_fs->tstore);
		}

		/*
		 * 从 tuplestore 获取下一条元组。
		 *
		 * 如果我们有函数的行计数，并且我们知道之前的读取位置超出了范围， 
		 * 那么就不要尝试读取。这允许在存在混合行计数的情况下正常进行反向扫描。
		 */
		if (fc_fs->rowcount != -1 && fc_fs->rowcount < fc_oldpos)
			ExecClearTuple(fc_fs->func_slot);
		else
			(void) tuplestore_gettupleslot(fc_fs->tstore,
										   ScanDirectionIsForward(fc_direction),
										   false,
										   fc_fs->func_slot);

		if (TupIsNull(fc_fs->func_slot))
		{
			/*
			 * 如果我们在正向方向上没有数据可供此函数使用，则现在我们知道它返回了多少行。
			 * 我们需要知道这一点以处理反向扫描。我们存储的行计数实际上是实际数字的 1+，
			 * 因为有时我们必须将 tuplestore 定位在其末尾的 1 个偏移量处。
			 */
			if (ScanDirectionIsForward(fc_direction) && fc_fs->rowcount == -1)
				fc_fs->rowcount = fc_node->ordinal;

			/*
			 * 用空值填充结果列
			 */
			for (fc_i = 0; fc_i < fc_fs->colcount; fc_i++)
			{
				fc_scanslot->tts_values[fc_att] = (Datum) 0;
				fc_scanslot->tts_isnull[fc_att] = true;
				fc_att++;
			}
		}
		else
		{
			/*
			 * 我们有一个结果，所以只需将其复制到结果列中。
			 */
			slot_getallattrs(fc_fs->func_slot);

			for (fc_i = 0; fc_i < fc_fs->colcount; fc_i++)
			{
				fc_scanslot->tts_values[fc_att] = fc_fs->func_slot->tts_values[fc_i];
				fc_scanslot->tts_isnull[fc_att] = fc_fs->func_slot->tts_isnull[fc_i];
				fc_att++;
			}

			/*
			 * 我们不会完成，直到每个函数结果都被用尽；我们用空值填充
			 * 短的结果，直到那时。
			 */
			fc_alldone = false;
		}
	}

	/*
	 * ordinal 列总是最后一个，根据规范。
	 */
	if (fc_node->ordinality)
	{
		fc_scanslot->tts_values[fc_att] = Int64GetDatumFast(fc_node->ordinal);
		fc_scanslot->tts_isnull[fc_att] = false;
	}

	/*
	 * 如果所有操作都完成，我们只需返回之前清除的扫描槽。否则，
	 * 完成创建虚拟元组。
	 */
	if (!fc_alldone)
		ExecStoreVirtualTuple(fc_scanslot);

	return fc_scanslot;
}

/*
 * FunctionRecheck -- 访问方法例程，用于在 EvalPlanQual 中重新检查元组
 */
static bool fc_FunctionRecheck(FunctionScanState *fc_node, TupleTableSlot *fc_slot)
{
	
	return true;
}

/* ----------------------------------------------------------------
 *		ExecFunctionScan(node)
 *
 *		顺序扫描函数并返回下一个符合条件的
 *		元组。
 *		我们调用 ExecScan() 例程，并传递适当的
 *		访问方法函数。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecFunctionScan(PlanState *fc_pstate)
{
	FunctionScanState *fc_node = castNode(FunctionScanState, fc_pstate);

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_FunctionNext,
					(ExecScanRecheckMtd) fc_FunctionRecheck);
}

/* ----------------------------------------------------------------
 *		ExecInitFunctionScan
 * ----------------------------------------------------------------
 */
FunctionScanState *
ExecInitFunctionScan(FunctionScan *fc_node, EState *fc_estate, int fc_eflags)
{
	FunctionScanState *fc_scanstate;
	int			fc_nfuncs = list_length(fc_node->functions);
	TupleDesc	fc_scan_tupdesc;
	int			fc_i,
				fc_natts;
	ListCell   *fc_lc;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & EXEC_FLAG_MARK));

	/*
	 * FunctionScan 不应有任何子节点。
	 */
	Assert(outerPlan(fc_node) == NULL);
	Assert(innerPlan(fc_node) == NULL);

	/*
	 * 为节点创建新的 ScanState
	 */
	fc_scanstate = makeNode(FunctionScanState);
	fc_scanstate->ss.ps.plan = (Plan *) fc_node;
	fc_scanstate->ss.ps.state = fc_estate;
	fc_scanstate->ss.ps.ExecProcNode = fc_ExecFunctionScan;
	fc_scanstate->eflags = fc_eflags;

	/*
	 * 我们是否添加一个序号列？
	 */
	fc_scanstate->ordinality = fc_node->funcordinality;

	fc_scanstate->nfuncs = fc_nfuncs;
	if (fc_nfuncs == 1 && !fc_node->funcordinality)
		fc_scanstate->simple = true;
	else
		fc_scanstate->simple = false;

	/*
	 * 序号 0 表示“在第一行之前”的位置。
	 *
	 * 即使在结果中不添加序号列，我们也需要跟踪序号位置，
	 * 以便正确处理多个结果大小不同的函数的向后扫描。
	 * （我们不能在任何单个函数的 tuplestore 中定位超过其
	 * 末尾 1 个位置，因此在向后扫描时，需要知道何时开始
	 * 再次包括该函数进行扫描。）
	 */
	fc_scanstate->ordinal = 0;

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_scanstate->ss.ps);

	fc_scanstate->funcstates = palloc(fc_nfuncs * sizeof(FunctionScanPerFuncState));

	fc_natts = 0;
	fc_i = 0;
	foreach(fc_lc, fc_node->functions)
	{
		RangeTblFunction *fc_rtfunc = (RangeTblFunction *) lfirst(fc_lc);
		Node	   *fc_funcexpr = fc_rtfunc->funcexpr;
		int			fc_colcount = fc_rtfunc->funccolcount;
		FunctionScanPerFuncState *fc_fs = &fc_scanstate->funcstates[fc_i];
		TupleDesc	fc_tupdesc;

		fc_fs->setexpr =
			ExecInitTableFunctionResult((Expr *) fc_funcexpr,
										fc_scanstate->ss.ps.ps_ExprContext,
										&fc_scanstate->ss.ps);

		/*
		 * 不要分配 tuplestore；实际的函数调用
		 * 会做到这一点。NULL 意味着我们还没有调用该函数
		 * （或者在重新扫描后需要再次调用它）。
		 */
		fc_fs->tstore = NULL;
		fc_fs->rowcount = -1;

		/*
		 * 现在构建一个 tupdesc，显示我们期望从函数中得到的
		 * 结果类型。如果我们有 coldeflist，则优先使用它
		 * （请注意，如果函数的名义输出类型是 RECORD，则解析器会强制
		 * 存在一个）。否则使用 get_expr_result_type。
		 *
		 * 请注意，如果函数返回一个命名的复合类型，它可能
		 * 现在包含比计划时更多或不同的列。对于这两种情况，
		 * 我们需要检查元组的兼容性。ExecMakeTableFunctionResult 处理
		 * 其中一些，而 CheckVarSlotCompatibility 提供了支持。
		 */
		if (fc_rtfunc->funccolnames != NIL)
		{
			fc_tupdesc = BuildDescFromLists(fc_rtfunc->funccolnames,
										 fc_rtfunc->funccoltypes,
										 fc_rtfunc->funccoltypmods,
										 fc_rtfunc->funccolcollations);

			/*
			 * 对于 RECORD 结果，确保已分配 typmod。
			 * （函数应该为自己做到这一点，但让我们做好准备以防
			 * 它没有做到。）
			 */
			BlessTupleDesc(fc_tupdesc);
		}
		else
		{
			TypeFuncClass fc_functypclass;
			Oid			fc_funcrettype;

			fc_functypclass = get_expr_result_type(fc_funcexpr,
												&fc_funcrettype,
												&fc_tupdesc);

			if (fc_functypclass == TYPEFUNC_COMPOSITE ||
				fc_functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
			{
				/* 复合数据类型，例如表的行类型 */
				Assert(fc_tupdesc);
				/* 必须从 typcache 中复制以确保安全 */
				fc_tupdesc = CreateTupleDescCopy(fc_tupdesc);
			}
			else if (fc_functypclass == TYPEFUNC_SCALAR)
			{
				/* 基本数据类型，即标量 */
				fc_tupdesc = CreateTemplateTupleDesc(1);
				TupleDescInitEntry(fc_tupdesc,
								   (AttrNumber) 1,
								   NULL,	/* 在这里不关心名称 */
								   fc_funcrettype,
								   -1,
								   0);
				TupleDescInitEntryCollation(fc_tupdesc,
											(AttrNumber) 1,
											exprCollation(fc_funcexpr));
			}
			else
			{
				/* 糟糕的错误信息，但解析器应该能捕获到这一点 */
				elog(ERROR, "function in FROM has unsupported return type");
			}
		}

		fc_fs->tupdesc = fc_tupdesc;
		fc_fs->colcount = fc_colcount;

		/*
		 * 仅当我们进行序号或多个函数时，我们才需要为功能结果
		 * 分别分配槽；否则，我们将直接将功能结果获取到扫描槽中。
		 */
		if (!fc_scanstate->simple)
		{
			fc_fs->func_slot = ExecInitExtraTupleSlot(fc_estate, fc_fs->tupdesc,
												   &TTSOpsMinimalTuple);
		}
		else
			fc_fs->func_slot = NULL;

		fc_natts += fc_colcount;
		fc_i++;
	}

	/*
	 * 创建合并的 TupleDesc
	 *
	 * 如果只有一个没有序号的函数，扫描结果
	 * tupdesc 即为函数结果 tupdesc --- 除非我们
	 * 可能会在下面填入新名称，因此去掉任何行类型标签。
	 */
	if (fc_scanstate->simple)
	{
		fc_scan_tupdesc = CreateTupleDescCopy(fc_scanstate->funcstates[0].tupdesc);
		fc_scan_tupdesc->tdtypeid = RECORDOID;
		fc_scan_tupdesc->tdtypmod = -1;
	}
	else
	{
		AttrNumber	fc_attno = 0;

		if (fc_node->funcordinality)
			fc_natts++;

		fc_scan_tupdesc = CreateTemplateTupleDesc(fc_natts);

		for (fc_i = 0; fc_i < fc_nfuncs; fc_i++)
		{
			TupleDesc	fc_tupdesc = fc_scanstate->funcstates[fc_i].tupdesc;
			int			fc_colcount = fc_scanstate->funcstates[fc_i].colcount;
			int			fc_j;

			for (fc_j = 1; fc_j <= fc_colcount; fc_j++)
				TupleDescCopyEntry(fc_scan_tupdesc, ++fc_attno, fc_tupdesc, fc_j);
		}

		/* 如果进行序号，最后添加一列类型为 "bigint" 的列 */
		if (fc_node->funcordinality)
		{
			TupleDescInitEntry(fc_scan_tupdesc,
							   ++fc_attno,
							   NULL,	/* 在这里不关心名称 */
							   INT8OID,
							   -1,
							   0);
		}

		Assert(fc_attno == fc_natts);
	}

	/*
	 * 初始化扫描槽和类型。
	 */
	ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss, fc_scan_tupdesc,
						  &TTSOpsMinimalTuple);

	/*
	 * 初始化结果槽、类型和投影。
	 */
	ExecInitResultTypeTL(&fc_scanstate->ss.ps);
	ExecAssignScanProjectionInfo(&fc_scanstate->ss);

	/*
	 * 初始化子表达式
	 */
	fc_scanstate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_scanstate);

	/*
	 * 创建一个内存上下文，以便 ExecMakeTableFunctionResult 可以
	 * 用于评估函数参数。我们不能为此使用每个元组的上下文，因为
	 * 它重置得太频繁；但是我们也不想让评估结果泄漏到查询生命周期上下文中。
	 * 我们只需要一个上下文，因为我们分别评估每个函数。
	 */
	fc_scanstate->argcontext = AllocSetContextCreate(CurrentMemoryContext,
												  "Table function arguments",
												  ALLOCSET_DEFAULT_SIZES);

	return fc_scanstate;
}

/* ----------------------------------------------------------------
 *		ExecEndFunctionScan
 *
 *		释放通过 C 例程分配的任何存储。
 * ----------------------------------------------------------------
 */
void ExecEndFunctionScan(FunctionScanState *fc_node)
{
	int			fc_i;

	/*
	 * 释放表达式上下文
	 */
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清空元组表
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);

	/*
	 * 释放槽和 tuplestore 资源
	 */
	for (fc_i = 0; fc_i < fc_node->nfuncs; fc_i++)
	{
		FunctionScanPerFuncState *fc_fs = &fc_node->funcstates[fc_i];

		if (fc_fs->func_slot)
			ExecClearTuple(fc_fs->func_slot);

		if (fc_fs->tstore != NULL)
		{
			tuplestore_end(fc_node->funcstates[fc_i].tstore);
			fc_fs->tstore = NULL;
		}
	}
}

/* ----------------------------------------------------------------
 *		ExecReScanFunctionScan
 *
 *		重新扫描关系。
 * ----------------------------------------------------------------
 */
void ExecReScanFunctionScan(FunctionScanState *fc_node)
{
	FunctionScan *fc_scan = (FunctionScan *) fc_node->ss.ps.plan;
	int			fc_i;
	Bitmapset  *fc_chgparam = fc_node->ss.ps.chgParam;

	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	for (fc_i = 0; fc_i < fc_node->nfuncs; fc_i++)
	{
		FunctionScanPerFuncState *fc_fs = &fc_node->funcstates[fc_i];

		if (fc_fs->func_slot)
			ExecClearTuple(fc_fs->func_slot);
	}

	ExecScanReScan(&fc_node->ss);

	/*
	 * 在这里，我们可以选择是否丢弃 tuplestores（并重新计算
	 * 函数输出）或仅仅重新扫描它们。如果表达式包含已更改的参数，
	 * 则必须重新计算，否则我们就重新扫描。
	 *
	 * XXX 也许如果函数是可变的，我们应该重新计算？但一般来说
	 * 执行器并不依据此条件化其操作。
	 */
	if (fc_chgparam)
	{
		ListCell   *fc_lc;

		fc_i = 0;
		foreach(fc_lc, fc_scan->functions)
		{
			RangeTblFunction *fc_rtfunc = (RangeTblFunction *) lfirst(fc_lc);

			if (bms_overlap(fc_chgparam, fc_rtfunc->funcparams))
			{
				if (fc_node->funcstates[fc_i].tstore != NULL)
				{
					tuplestore_end(fc_node->funcstates[fc_i].tstore);
					fc_node->funcstates[fc_i].tstore = NULL;
				}
				fc_node->funcstates[fc_i].rowcount = -1;
			}
			fc_i++;
		}
	}

	/* 重置序号计数器 */
	fc_node->ordinal = 0;

	/* 确保我们回放任何剩余的 tuplestores */
	for (fc_i = 0; fc_i < fc_node->nfuncs; fc_i++)
	{
		if (fc_node->funcstates[fc_i].tstore != NULL)
			tuplestore_rescan(fc_node->funcstates[fc_i].tstore);
	}
}
